//
// Copyright (C) 2004-2010  Autodesk, Inc.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of version 2.1 of the GNU Lesser
// General Public License as published by the Free Software Foundation.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#include <stdafx.h>
#include "FdoCMSDelegate.h"
#include <complex>

#ifndef _WIN32
#define __max(a,b)  (((a) > (b)) ? (a) : (b))
#define __min(a,b)  (((a) < (b)) ? (a) : (b))
#endif

FdoInt32 FdoCMSDelegate::_CalculateZoomLevel(const FdoCMSBoundingBox* bbox
                                             , FdoSize expectedImageHeight
                                             , FdoSize expectedImageWidth
                                             , FdoInt32 maxZoomLevel
                                             , FdoSize& imageHeightOut
                                             , FdoSize& imageWidthOut)
{
    const double dGapZoomlevel = 0.0;
    double GetMinX, GetMinY, GetMaxX, GetMaxY;

    GetMinX = bbox->GetMinX();
    GetMinY = bbox->GetMinY();
    GetMaxX = bbox->GetMaxX();
    GetMaxY = bbox->GetMaxY();

    int pixelX_min,pixelX_max, pixelY_min,pixelY_max;
    FdoCMSBoundingBox::LatLongToPixelXY(GetMaxY, GetMinX, maxZoomLevel,pixelX_min,pixelY_min);
    FdoCMSBoundingBox::LatLongToPixelXY(GetMinY, GetMaxX, maxZoomLevel,pixelX_max,pixelY_max);

    double pixelMAX = 512.0;
    for(int i = 1; i<maxZoomLevel; i++)
        pixelMAX = pixelMAX * 2.0;

    double gapX = ((double)(pixelX_max - pixelX_min + 1)) / ((double)expectedImageWidth);
    gapX = abs(gapX);
    double dX = maxZoomLevel - ::log(gapX) / ::log(2.0) + dGapZoomlevel;
    int zoomlevelX = __max((int)dX, 1);

    double gapY = ((double)(pixelY_max - pixelY_min + 1)) / ((double)expectedImageHeight);
    gapY = abs(gapY);
    double dY = maxZoomLevel - ::log(gapY) / ::log(2.0) + dGapZoomlevel;
    int zoomlevelY = __max((int)dY, 1);

    int zoomlevel = __min(zoomlevelY, zoomlevelX);
    if(zoomlevel < 1)
        zoomlevel = 1;

    FdoCMSBoundingBox::LatLongToPixelXY(GetMaxY, GetMinX, zoomlevel,pixelX_min,pixelY_min);
    FdoCMSBoundingBox::LatLongToPixelXY(GetMinY, GetMaxX, zoomlevel,pixelX_max,pixelY_max);

    imageHeightOut = abs(pixelY_max - pixelY_min);  // +1 needed?
    imageWidthOut = abs(pixelX_max - pixelX_min);  // +1 needed?

    return zoomlevel;
}

void FdoCMSDelegate::VerifyImageSize(FdoSize& imageHeightOut, FdoSize& imageWidthOut)
{
    imageHeightOut = max(imageHeightOut, MinImageSize());
    imageWidthOut = max(imageWidthOut, MinImageSize());
}

bool
FdoCMSDelegate::SplitImageNeeded(FdoCMSBoundingBox* bbox
                                 , FdoSize expectedImageHeight
                                 , FdoSize expectedImageWidth
                                 , FdoInt32 maxZoomLevel
                                 , FdoSize& imageHeightOut
                                 , FdoSize& imageWidthOut
                                 , FdoInt32& zoomLevelOut)
{
    bbox->Verify();
    zoomLevelOut = _CalculateZoomLevel(bbox, expectedImageHeight, expectedImageWidth, maxZoomLevel, imageHeightOut, imageWidthOut);
    VerifyImageSize(imageHeightOut, imageWidthOut);
    return true;
    //// 1 Pixel difference is tolerant.
    //if(imageHeightOut > MaxImageSize()+1 || imageWidthOut > MaxImageSize()+1)
    //    return true;
    //else
    //    return false;
}

FdoCMSImageSpaceTileManager*
FdoCMSDelegate::SplitImage(FdoCMSBoundingBox* bbox
                           , FdoInt32 zoomLevel
                           , FdoSize heightMatched
                           , FdoSize widthMatched)
{
    //if(!(heightMatched > MaxImageSize()+1 || widthMatched > MaxImageSize()+1))
    //    return NULL;

    FdoCMSImageSpaceTileManager* pTileManager = new FdoCMSImageSpaceTileManager(heightMatched, widthMatched);
    FdoSize countY = heightMatched / MaxImageSize();
    if(heightMatched % MaxImageSize() >= MinImageSize())
        countY++;
    FdoSize countX = widthMatched / MaxImageSize();
    if(widthMatched % MaxImageSize() >= MinImageSize())
        countX++;
    if(countY == 0)
        countY = 1;
    if(countX == 0)
        countX = 1;

    pTileManager->SetXYCount(countX, countY);
    pTileManager->BeginWrite();

    double GetMinX, GetMinY, GetMaxX, GetMaxY;
    FdoInt32 pixelX_min,pixelX_max, pixelY_min,pixelY_max;
    GetMinX = bbox->GetMinX();
    GetMinY = bbox->GetMinY();
    GetMaxX = bbox->GetMaxX();
    GetMaxY = bbox->GetMaxY();

    FdoCMSBoundingBox::LatLongToPixelXY(GetMaxY, GetMinX, zoomLevel,pixelX_min,pixelY_min);
    FdoCMSBoundingBox::LatLongToPixelXY(GetMinY, GetMaxX, zoomLevel,pixelX_max,pixelY_max);

    FdoSize pixelX_start = 0;
    FdoSize pixelY_start = 0;
    FdoSize pixelX_min_now;
    FdoSize pixelY_min_now;
    FdoSize pixelX_max_now, pixelY_max_now;
    for(FdoSize indexY = 0; indexY < countY; indexY++)
    {
        pixelX_start = 0;

        pixelY_min_now = pixelY_start + pixelY_min;

        if(indexY == countY - 1)
            pixelY_max_now = pixelY_min + heightMatched - 1;
        else
            pixelY_max_now = pixelY_min_now + MaxImageSize() - 1;
        for(int indexX = 0; indexX < countX; indexX++)
        {
            pixelX_min_now = pixelX_min + pixelX_start;
            // last one
            if(indexX == countX - 1)
                pixelX_max_now = pixelX_min + widthMatched - 1;
            else
                pixelX_max_now = pixelX_min_now + MaxImageSize() - 1;

            double latitudeMin, latitudeMax, longitudeMin, longitudeMax;
            FdoCMSBoundingBox::PixelXYToLatLong(pixelX_min_now, pixelY_max_now, zoomLevel, latitudeMin, longitudeMin);
            FdoCMSBoundingBox::PixelXYToLatLong(pixelX_max_now, pixelY_min_now, zoomLevel, latitudeMax, longitudeMax);

            FdoSize heightY = pixelY_max_now - pixelY_min_now + 1;
            FdoSize widthX = pixelX_max_now - pixelX_min_now + 1;

            FdoSize indexXOut, indexYOut;
            if(heightY > 0 && widthX > 0)
            {
                FdoCMSImageSpaceTile* spaceTile = 
                    new FdoCMSImageSpaceTile(longitudeMin, longitudeMax, latitudeMin, latitudeMax, 
                    heightY, widthX, pixelX_start, pixelY_start);

                pTileManager->AppendTile(spaceTile, indexXOut, indexYOut);
            }
            else
            {
                assert(0);
                pTileManager->AppendTile(NULL, indexXOut, indexYOut);
            }
            //x min increase;
            pixelX_min_now = pixelX_max_now + 1;
            pixelX_start += MaxImageSize();
        }
        // y min increase
        pixelY_min_now = pixelY_max_now + 1;
        // reset the x min
        pixelX_min_now = pixelX_min;

        pixelY_start += MaxImageSize();
    }
    pTileManager->EndWrite();
    return pTileManager;
}

FdoSize FdoCMSDelegate::MaxImageSize()
{
    static const FdoSize dMaxSize = 512; 
    return dMaxSize;
}

FdoSize FdoCMSDelegate::MinImageSize()
{
    static const FdoSize dMaxSize = 80; 
    return dMaxSize;
}

